/******************************************************************************
 Copyright (c) 2009-2022 Qualcomm Technologies International, Ltd.
 All Rights Reserved.
 Qualcomm Technologies International, Ltd. Confidential and Proprietary.

 REVISION:      $Revision: #9 $
******************************************************************************/

#include "csr_synergy.h"

#include "csr_bt_cm_main.h"
#include "csr_bt_cm_l2cap.h"
#include "csr_bt_cm_lib.h"
#include "csr_bt_cm_private_lib.h"
#include "csr_bt_cm_rfc.h"
#include "csr_bt_cm_dm.h"
#include "csr_bt_cm_sdc.h"
#include "csr_bt_cm_util.h"

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
#include "csr_bt_cm_bnep.h"
#endif

#include "csr_bt_cm_events_handler.h"

#ifdef CSR_BT_INSTALL_CM_LINK_POLICY
static void insertDefaultLinkPolicySettings(cmInstanceData_t *cmData)
{
    CsrUintFast8 i;
    aclTable  *aclConnectionElement = NULL;

    for ( i = 0; i < NUM_OF_ACL_CONNECTION; i++)
    {
        if (!CsrBtBdAddrEqZero(&(cmData->roleVar.aclVar[i].deviceAddr)))
        {
            aclConnectionElement                        = &(cmData->roleVar.aclVar[i]);
            aclConnectionElement->linkPolicySettings    = cmData->dmVar.defaultLinkPolicySettings;

#ifndef CSR_BT_INSTALL_LP_POWER_TABLE
            aclConnectionElement->sniffSettings         = cmData->dmVar.defaultSniffSettings;
#endif
        }
    }
}

static void csrBtCmWriteLinkPolicyErrorIndMsgSend(CsrSchedQid appHandle,
                                                  CsrBtDeviceAddr deviceAddr,
                                                  CsrBtResultCode resultCode,
                                                  CsrBtSupplier resultSupplier)
{
    CsrBtCmWriteLinkPolicyErrorInd * prim;

    prim                 = (CsrBtCmWriteLinkPolicyErrorInd *)CsrPmemAlloc(sizeof(CsrBtCmWriteLinkPolicyErrorInd));
    prim->type           = CSR_BT_CM_WRITE_LINK_POLICY_ERROR_IND;
    prim->deviceAddr     = deviceAddr;
    prim->resultCode     = resultCode;
    prim->resultSupplier = resultSupplier;
    CsrBtCmPutMessage(appHandle, prim);
}
#endif

#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
#ifndef EXCLUDE_CSR_BT_RFC_MODULE
static void csrBtCmWriteLpSettingsCompleteRfcHandler(cmInstanceData_t *cmData)
{
    cmRfcConnElement * theRfcElement = CM_RFC_ELEMENT_ACTIVE(cmData);

    if (theRfcElement && theRfcElement->cmRfcConnInst)
    {
        cmRfcConnInstType *theLogicalLink = theRfcElement->cmRfcConnInst;

        switch(theLogicalLink->state)
        {
            case CSR_BT_CM_RFC_STATE_CONNECT_INIT:
                { /* The CM has just ensure that the DM only will accept ACTIVE link
                     policy, in order to prevent a link policy change, while a RFCOMM
                     connection is establish */

                    if (!cmData->rfcVar.cancelConnect)
                    {
                        aclTable       * aclConnectionElement;
                        returnAclConnectionElement(cmData, theLogicalLink->deviceAddr, &aclConnectionElement);

                        if (aclConnectionElement)
                        {
                            /*CSR_BT_CM_STATE_CHANGE(theLogicalLink->state, CSR_BT_CM_RFC_STATE_CONNECTED);
                            CsrBtCmConnectCfmMsgSend(cmData, CSR_BT_RESULT_CODE_CM_SUCCESS, CSR_BT_SUPPLIER_CM);*/

                            CSR_BT_CM_STATE_CHANGE(theLogicalLink->state,
                                                   CSR_BT_CM_RFC_STATE_CONNECT);

                            CsrBtCmRfcStartInitiateConnection(cmData, theLogicalLink);
                            CsrBtCmDmLocalQueueHandler();
                        }
                        else
                        {
                            /* Existing connection has been terminated, setup a new connection */
                            if (CsrBtCmDmWriteKnownCacheParamsDirect(cmData,
                                                                     theLogicalLink->deviceAddr,
                                                                     RFC_PLAYER,
                                                                     0))
                            {
                                /* Wait for application of cached parameters */
                            }
                            else
                            {
                                CSR_BT_CM_STATE_CHANGE(theLogicalLink->state, CSR_BT_CM_RFC_STATE_CONNECT);
                                CsrBtCmRfcStartInitiateConnection(cmData, theLogicalLink);
                                CsrBtCmDmLocalQueueHandler();
                            }
                        }
                    }
                    else
                    { /* The application has requested to cancel the connection */
#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
                        CsrUint8 numOfConnection = CsrBtCmReturnNumOfConnectionsToPeerDevice(cmData,
                                                                                             theLogicalLink->deviceAddr);

                        if (numOfConnection)
                        { /* Currently there is at least one more connection attach to the
                             device address. Ensure that SNIFF is supported */
                            cmData->smVar.arg.result.code        = CSR_BT_RESULT_CODE_CM_CANCELLED;
                            cmData->smVar.arg.result.supplier    = CSR_BT_SUPPLIER_CM;
                            CsrBtCmDmWriteLpSettingsAllMsgSend(cmData,
                                                               theLogicalLink->deviceAddr,
                                                               KEEP_CURRENT_PLAYER);
                            CSR_BT_CM_STATE_CHANGE(theLogicalLink->state, CSR_BT_CM_RFC_STATE_CONNECT);
                        }
                        else
#endif /* CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */
                        { /* There is no other connection attach to the device address.
                             Inform the application. */
                            CsrBtCmConnectCfmMsgSend(cmData, CSR_BT_RESULT_CODE_CM_CANCELLED, CSR_BT_SUPPLIER_CM);
                            CsrBtCmDmLocalQueueHandler();
                        }
                    }
                    break;
                }
            case CSR_BT_CM_RFC_STATE_CONNECT:
                {/* The attempt to establish a RFC connection is finish. Inform the
                    application about the result and restore the local DM and service
                    manager queue */
                    if (cmData->smVar.arg.result.code == CSR_BT_RESULT_CODE_CM_SUCCESS &&
                        cmData->smVar.arg.result.supplier == CSR_BT_SUPPLIER_CM)
                    {
                        if (!cmData->rfcVar.cancelConnect)
                        {
                            CSR_BT_CM_STATE_CHANGE(theLogicalLink->state,
                                                   CSR_BT_CM_RFC_STATE_CONNECTED);
                            CsrBtCmConnectCfmMsgSend(cmData,
                                                     cmData->smVar.arg.result.code,
                                                     cmData->smVar.arg.result.supplier);
                        }
                        else
                        {
                            CSR_BT_CM_STATE_CHANGE(cmData->rfcVar.connectState, CM_RFC_CANCELING);
                            RfcDisconnectReqSend((CsrUint16)theLogicalLink->btConnId);
                        }
                    }
                    else
                    { /* The attempt to establish a RFC connection fail. Check if the link policy need to be change */
                        CSR_BT_CM_STATE_CHANGE(theLogicalLink->state, CSR_BT_CM_RFC_STATE_IDLE);
                        CsrBtCmModeChangeReqMsgSend(cmData, theLogicalLink->deviceAddr);
                        CsrBtCmConnectCfmMsgSend(cmData,
                                                 cmData->smVar.arg.result.code,
                                                 cmData->smVar.arg.result.supplier);
                    }
                    CsrBtCmDmLocalQueueHandler();
                    break;
                }

            case CSR_BT_CM_RFC_STATE_RELEASE_INIT:
                { /* Has just ensure that the link policy can not be change, while the RFC
                     connection    is release */
                CSR_BT_CM_STATE_CHANGE(theLogicalLink->state,
                                       CSR_BT_CM_RFC_STATE_RELEASE);

#ifndef EXCLUDE_CSR_BT_SCO_MODULE
                    if (theLogicalLink->eScoParms &&
                        theLogicalLink->eScoParms->handle < SCOBUSY_ACCEPT)
                    { /* Before closing down the RFC connection the SCO connection
                         has to be release. */
                        cmData->dmVar.lockMsg = CSR_BT_CM_SCO_DISCONNECT_REQ;
                        cmData->dmVar.rfcConnIndex  = theRfcElement->elementId;
                        theLogicalLink->eScoParms->closeSco = TRUE;
                        dm_sync_disconnect_req(theLogicalLink->eScoParms->handle,
                                               HCI_ERROR_OETC_USER);
                    }
                    else
#endif
                    { /* There is no SCO connection, just close down the RFC
                         connection. The call is confirmed by RFC_RELEASE_IND. */
                        RfcDisconnectReqSend((CsrUint16)theLogicalLink->btConnId);
                        CsrBtCmDmLocalQueueHandler();
                    }
                    break;
                }
            case CSR_BT_CM_RFC_STATE_RELEASE_FINAL:
                {
                    CsrBool  status  = CsrBtCmRfcReleaseStatus((CsrBtReasonCode) cmData->smVar.arg.result.code,
                                                               cmData->smVar.arg.result.supplier);

                    CsrBtCmModeChangeReqMsgSend(cmData, theLogicalLink->deviceAddr);
                    CsrBtCmDmSyncClearPcmSlotFromTable(cmData,
                                                       theLogicalLink->eScoParms);

                    CsrBtCmDisconnectIndMsgCleanupSend(cmData,
                                                       theRfcElement,
                                                       (CsrBtReasonCode) cmData->smVar.arg.result.code,
                                                       cmData->smVar.arg.result.supplier,
                                                       status,
                                                       TRUE);

                    CsrBtCmServiceManagerLocalQueueHandler(cmData);
                    CsrBtCmDmLocalQueueHandler();
                    break;
                }
            case CSR_BT_CM_RFC_STATE_CONNECT_ACCEPT:
            case CSR_BT_CM_RFC_STATE_CONNECT_ACCEPT_FINAL:
                { /* The CM has just ensure that the host controller will accept
                     CSR_BT_SNIFF_MODE doing the CSR_BT_CM_CONNECT_ACCEPT_REQ routine */
                    CsrBtCmConnectAcceptCfmMsgSend(cmData,
                                                   theRfcElement,
                                                   cmData->smVar.arg.result.code,
                                                   cmData->smVar.arg.result.supplier);
                    CsrBtCmServiceManagerLocalQueueHandler(cmData);
                    CsrBtCmDmLocalQueueHandler();
                    break;
                }
            default :
                {
                    CsrBtCmServiceManagerLocalQueueHandler(cmData);
                    CsrBtCmDmLocalQueueHandler();
                    break;
                }
        }
    }
    else
    {
        if (theRfcElement)
        { /* If theRfcElement == NULL then this function has allready been called */
            CsrBtCmServiceManagerLocalQueueHandler(cmData);
        }
        CsrBtCmDmLocalQueueHandler();
    }
}
#endif /* EXCLUDE_CSR_BT_RFC_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
static void cmWriteLpSettingsCompleteL2capHandler(cmInstanceData_t *cmData)
{
    cmL2caConnElement * theL2caElement = CM_L2CA_ELEMENT_ACTIVE(cmData);

    if (theL2caElement && theL2caElement->cmL2caConnInst)
    {
        cmL2caConnInstType *l2capLink = theL2caElement->cmL2caConnInst;
        CsrBtDeviceAddr remoteAddr = l2capLink->deviceAddr;

        switch(l2capLink->state)
        {
            case CSR_BT_CM_L2CAP_STATE_RELEASE_FINAL:
                { /* The CM has just ensure that SNIFF mode is supported after
                     the connection is release. Inform the application and restore the
                     local service manager and DM queue */
                    CsrBtCmModeChangeReqMsgSend(cmData, l2capLink->deviceAddr);
                    CsrBtCmL2capDisconnectIndMsgSend(cmData,
                                                     theL2caElement,
                                                     (CsrBtReasonCode) cmData->smVar.arg.result.code,
                                                     cmData->smVar.arg.result.supplier,
                                                     TRUE,
                                                     0); /* Signal ID is only used for sending response */

                    CsrBtCmWriteAutoFlushTimeout(cmData, &remoteAddr);
                    CsrBtCmDmLocalQueueHandler();
                    CsrBtCmServiceManagerLocalQueueHandler(cmData);
                    break;
                }
            case CSR_BT_CM_L2CAP_STATE_RELEASE_INIT:
                { /* The CM has just ensure that the link policy can not be change, while it
                     disconnect a L2cap connection */
                CSR_BT_CM_STATE_CHANGE(l2capLink->state,
                                       CSR_BT_CM_L2CAP_STATE_RELEASE);

                    /* Release the l2cap connection on restore the local DM queue */
                    L2CA_DisconnectReq(CM_GET_UINT16ID_FROM_BTCONN_ID(l2capLink->btConnId));
                    CsrBtCmDmLocalQueueHandler();
                    break;
                }
            case CSR_BT_CM_L2CAP_STATE_RELEASE_INIT_XOVER:
                { /* Crossover between local and remote disconnect. The CM has just
                     ensure that low power mode is not supported. Make sure that low
                     power mode is supported, and goto CSR_BT_CM_L2CAP_STATE_RELEASE_FINAL */
                    CsrBtCmL2caFinalReleaseHandler(cmData,
                                                   theL2caElement,
                                                   TRUE,
                                                   (CsrBtReasonCode) cmData->smVar.arg.result.code,
                                                   cmData->smVar.arg.result.supplier,
                                                   FALSE,
                                                   0); /* Signal ID is only used for sending response */
                    CsrBtCmDmLocalQueueHandler();
                    break;
                }
            case CSR_BT_CM_L2CAP_STATE_CONNECT_INIT:
                {/* The CM has just ensure that the DM only will accept ACTIVE link
                    policy, in order to prevent a link policy change, while a L2CAP
                    connection is establish */

                    if (cmData->l2caVar.cancelConnect)
                    {
                        CsrBtCmL2caConnectCancelCleanup(cmData, theL2caElement, TRUE);
                    }
#ifndef EXCLUDE_CSR_BT_CM_LEGACY_PAIRING_DETACH
                    else
                    {
                        /* Check whether we need to apply the legacy
                         * pairing ACL detach work around */
                        if(!CsrBtCmL2caCheckLegacyDetach(cmData, l2capLink))
                        {
                            CsrBtCml2caAutoConnectSetup(cmData, theL2caElement);
                            CsrBtCmDmLocalQueueHandler();
                        }
                    }
#endif /* !EXCLUDE_CSR_BT_CM_LEGACY_PAIRING_DETACH */
                    break;
                }
            case CSR_BT_CM_L2CAP_STATE_CONNECT:
                {/* The attempt to establish a L2CAP connection is finish. Inform the
                    application about the result, and restore the local service manager and DM
                    queue */
                    if (cmData->smVar.arg.result.code     == CSR_BT_RESULT_CODE_CM_SUCCESS &&
                        cmData->smVar.arg.result.supplier == CSR_BT_SUPPLIER_CM)
                    {
                        /* A L2CAP connection has been establish with
                         * success. Inform the application and restore
                         * the local DM and service manager queue */
                        if (!cmData->l2caVar.cancelConnect)
                        {
                            CSR_BT_CM_STATE_CHANGE(l2capLink->state,
                                                   CSR_BT_CM_L2CAP_STATE_CONNECTED);
                            CsrBtCmL2caConnectCfmMsgHandler(cmData, theL2caElement,
                                                            CSR_BT_RESULT_CODE_CM_SUCCESS, CSR_BT_SUPPLIER_CM);
                            CsrBtCmWriteAutoFlushTimeout(cmData, &remoteAddr);
                            
#ifndef CSR_STREAMS_ENABLE
                            CsrBtCmL2caDataStart(cmData, l2capLink);
#endif
                        }
                        else
                        {
                            L2CA_DisconnectReq(CM_GET_UINT16ID_FROM_BTCONN_ID(l2capLink->btConnId));
                        }
                    }
                    else
                    { /* The attempt to establish a l2cap connection fail. Inform the
                         application, clean up the l2cap connection table and restore
                         the local service manager and DM queue */
                        CSR_BT_CM_STATE_CHANGE(l2capLink->state, CSR_BT_CM_L2CAP_STATE_IDLE);
                        CsrBtCmModeChangeReqMsgSend(cmData, l2capLink->deviceAddr);
                        CsrBtCmL2caConnectCfmMsgHandler(cmData,
                                                        theL2caElement,
                                                        cmData->smVar.arg.result.code,
                                                        cmData->smVar.arg.result.supplier);
                    }
                    CsrBtCmDmLocalQueueHandler();
                    break;
                }
            case CSR_BT_CM_L2CAP_STATE_CONNECT_ACCEPT_FINAL:
                {
                    CSR_BT_CM_STATE_CHANGE(l2capLink->state,
                                           CSR_BT_CM_L2CAP_STATE_CONNECTED);
                    CsrBtCmL2caConnectAcceptCfmHandler(cmData, theL2caElement,
                                                       CSR_BT_RESULT_CODE_CM_SUCCESS, CSR_BT_SUPPLIER_CM);
                    CsrBtCmWriteAutoFlushTimeout(cmData, &remoteAddr);
                    CsrBtCmDmLocalQueueHandler();
                    CsrBtCmServiceManagerLocalQueueHandler(cmData);
                    break;
                }
            case CSR_BT_CM_L2CAP_STATE_SAVE_DISCONNECT:
                {
                    CsrBtCmL2caConnectAcceptCfmHandler(cmData, theL2caElement,
                                                       CSR_BT_RESULT_CODE_CM_SUCCESS, CSR_BT_SUPPLIER_CM);
                    CsrBtCmDmLocalQueueHandler();
                    CsrBtCmServiceManagerLocalQueueHandler(cmData);
                    break;
                }
            default :
                {
                    CsrBtCmServiceManagerLocalQueueHandler(cmData);
                    CsrBtCmDmLocalQueueHandler();
                    break;
                }
        }
    }
    else
    {
        if (theL2caElement)
        { /* If theL2caElement == NULL then this function has allready been called */
            CsrBtCmServiceManagerLocalQueueHandler(cmData);
        }
        CsrBtCmDmLocalQueueHandler();
    }
}
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
static void csrBtCmWriteLpSettingsCompleteBnepHandler(cmInstanceData_t *cmData)
{
    DM_HCI_WRITE_LINK_POLICY_SETTINGS_CFM_T        *dmPrim;
    CsrUint8                                    theIndex;
    bnepTable *bnepConnection;

    dmPrim        = (DM_HCI_WRITE_LINK_POLICY_SETTINGS_CFM_T *) cmData->recvMsgP;
    theIndex    = returnIndexToThisBdAddr(cmData, dmPrim->bd_addr);
    bnepConnection = &(cmData->bnepVar.connectVar[theIndex]);

    switch(cmData->bnepVar.connectVar[theIndex].state)
    {
        case CSR_BT_CM_BNEP_STATE_CONNECT_INIT:
            {/* The CM has just ensure that the DM only will accept ACTIVE link
                policy, in order to prevent a link policy change, while a BNEP
                connection is establish */
                CSR_BT_CM_STATE_CHANGE(cmData->bnepVar.connectVar[theIndex].state,
                                       CSR_BT_CM_BNEP_STATE_CONNECT);
                CSR_BT_CM_STATE_CHANGE(cmData->bnepVar.connectState, CM_BNEP_CONNECT);

                if (!cmData->bnepVar.cancelConnect)
                {
                    cmData->bnepVar.connectVar[theIndex].actualMode    = CSR_BT_ACTIVE_MODE;
                    if (!cmData->bnepVar.moreLpSettingsInProgress)
                    {
                        CsrBtBnepConnectReqSend(cmData->bnepVar.connectReqFlags, CsrBtBdAddrToEtherAddr(&cmData->bnepVar.connectVar[theIndex].deviceAddr));
                    }
                    CsrBtCmDmLocalQueueHandler();
                }
                else
                {
                    cmData->smVar.arg.result.code     = CSR_BT_RESULT_CODE_CM_CANCELLED;
                    cmData->smVar.arg.result.supplier = CSR_BT_SUPPLIER_CM;

                    CsrBtCmBnepConnectIndMsgSend(cmData,
                                                 cmData->bnepVar.appHandle,
                                                 ID_EMPTY,
                                                 CsrBtBdAddrToEtherAddr(&bnepConnection->deviceAddr),
                                                 0,
                                                 0,
                                                 0,
                                                 cmData->smVar.arg.result.code,
                                                 cmData->smVar.arg.result.supplier);

#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
                    if (CsrBtCmReturnNumOfConnectionsToPeerDevice(cmData,
                                                                  bnepConnection->deviceAddr))
                    { /* Currently there is at least one more connection attach to the
                         device address. Ensure that SNIFF is supported            */
                        CsrBtCmDmWriteLpSettingsAllMsgSend(cmData,
                                                           bnepConnection->deviceAddr,
                                                           KEEP_CURRENT_PLAYER);
                        CSR_BT_CM_STATE_CHANGE(bnepConnection->state,
                                               CSR_BT_CM_BNEP_STATE_CONNECT_CFM);
                    }
                    else
#endif /* CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */
                    { /* There is no other connection attach to the device address.
                         Inform the application, restore the local service manager
                         queue, and clear up the l2cap connection table                    */
                        CsrBtCmBnepClearBnepTableIndex(bnepConnection);
                        CsrBtCmDmLocalQueueHandler();
                        CsrBtCmServiceManagerLocalQueueHandler(cmData);
                    }
                    CsrBtCmDmUpdateAndClearCachedParamReqSend(bnepConnection->deviceAddr);
                }
                break;
            }
        case CSR_BT_CM_BNEP_STATE_CONNECT_CFM:
            {/* The attempt to establish a BNEP connection is finish. Inform the
                application about the result, and restore the local service manager and DM
                queue */
                if (cmData->smVar.arg.result.code     == CSR_BT_RESULT_CODE_CM_SUCCESS &&
                    cmData->smVar.arg.result.supplier == CSR_BT_SUPPLIER_CM)
                { /* A BNEP connection has been establish with success.
                     Restore the local DM and service manager queue */
                    CSR_BT_CM_STATE_CHANGE(cmData->bnepVar.connectVar[theIndex].state,
                                           CSR_BT_CM_BNEP_STATE_CONNECTED);
                    cmData->bnepVar.moreLpSettingsInProgress        = FALSE;
                }
                else
                { /* The attempt to establish a bnep connection fail. Clean up the
                     bnep connection table and restore the local service manager and DM queue */
                    CSR_BT_CM_STATE_CHANGE(cmData->bnepVar.connectVar[theIndex].state, CSR_BT_CM_BNEP_STATE_IDLE);
                    CsrBtCmModeChangeReqMsgSend(cmData, cmData->bnepVar.indexPtr->deviceAddr);
                    CsrBtCmBnepClearBnepTableIndex(cmData->bnepVar.indexPtr);
                }
                CsrBtCmServiceManagerLocalQueueHandler(cmData);
                CsrBtCmDmLocalQueueHandler();
                break;
            }
        case CSR_BT_CM_BNEP_STATE_CONNECT_ACCEPT_FINAL:
            {
                /* The BNEP connection is now establish with success. Inform the
                   application, and restore the  local service manager and DM queue */
#ifndef CSR_BT_INSTALL_LP_POWER_TABLE
                CsrUint8        numOfConnection;
                CsrBool         smUnlock = TRUE;
#endif
                CsrUint8        newIndex;

                CSR_BT_CM_STATE_CHANGE(cmData->bnepVar.connectVar[theIndex].state, CSR_BT_CM_BNEP_STATE_CONNECTED);

                newIndex = returnEmptyBnepIdIndex(cmData);
                if(newIndex != CM_ERROR)
                { /* If it is possible to create a new bnep connection */
                    CsrBtCmBnepConnectAcceptReqInitHandler(cmData, newIndex, cmData->bnepVar.classOfDevice);
                }

#ifndef CSR_BT_INSTALL_LP_POWER_TABLE
                numOfConnection        = CsrBtCmReturnNumOfConnectionsToPeerDevice(cmData, cmData->bnepVar.connectVar[theIndex].deviceAddr);

                if(numOfConnection != 0)
                {
                    cmData->bnepVar.connectVar[theIndex].actualMode = CmDmGetActualMode(cmData, &cmData->bnepVar.connectVar[theIndex].deviceAddr);

                    if(cmData->bnepVar.connectVar[theIndex].actualMode != CSR_BT_ACTIVE_MODE)
                    {
                        /* Check if we can move to ACTIVE mode. */
                        if (CmDmPowerTableHandleBnepConnectedState(cmData, &cmData->bnepVar.connectVar[theIndex], CSR_BT_ACTIVE_MODE))
                        {
                            /* There is power table change expected, SM queue will be freed from respective mode change indication handling. */
                            smUnlock = FALSE;
                        }
                    }
                }
#endif /* !CSR_BT_INSTALL_LP_POWER_TABLE */

#ifndef CSR_BT_INSTALL_LP_POWER_TABLE
                if (smUnlock)
#endif
                {
                    CsrBtCmServiceManagerLocalQueueHandler(cmData);
                }

                CsrBtCmDmLocalQueueHandler();
                break;
            }
        case CSR_BT_CM_BNEP_STATE_DISCONNECT_INIT:
            { /* The CM has just ensure that the link policy can not be change, while it
                 disconnect a bnep connection */
                CSR_BT_CM_STATE_CHANGE(cmData->bnepVar.connectVar[theIndex].state,
                                       CSR_BT_CM_BNEP_STATE_DISCONNECT_REQ);
                CsrBtBnepDisconnectReqSend(0, cmData->bnepVar.connectVar[theIndex].id);

                if (cmData->bnepVar.disconnectInd)
                {
                    cmData->bnepVar.disconnectInd = FALSE;

#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
                    if (CsrBtCmReturnNumOfConnectionsToPeerDevice(cmData,
                                                                  cmData->bnepVar.connectVar[theIndex].deviceAddr))
                    { /* Currently there is at least one more connection attach to the given device
                         address. Ensure that the SNIFF mode is supported */
                        CsrBtCmDmWriteLpSettingsAllMsgSend(cmData,
                                                           cmData->bnepVar.connectVar[theIndex].deviceAddr,
                                                           BNEP_PLAYER);
                    }
                    else
#endif /* CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */
                    { /* Currently there is no other connections attach to the given Bluetooth
                         address. clean up the bnep connection table, and restore the local service manager queue */
                        CsrBtCmBnepClearBnepTableIndex(&cmData->bnepVar.connectVar[theIndex]);
                        CsrBtCmServiceManagerLocalQueueHandler(cmData);
                        CsrBtCmDmLocalQueueHandler();
                    }
                }
                break;
            }
        case CSR_BT_CM_BNEP_STATE_DISCONNECT_REQ:
            { /* The CM has just ensure that SNIFF mode is supported after
                 the connection is release. If requested mode is different from active,
                 change to requested mode and restore the local service manager and DM queue */
                /* Since the Connection is already disconnected, 
                 * Clear the flag cmData->bnepVar.moreLpSettingsInProgress,
                 * Else future Connect requests will not be sent to BNEP layer.
                 */
                cmData->bnepVar.moreLpSettingsInProgress = FALSE;
                CSR_BT_CM_STATE_CHANGE(cmData->bnepVar.connectVar[theIndex].state, CSR_BT_CM_BNEP_STATE_IDLE);
                CsrBtCmModeChangeReqMsgSend(cmData, cmData->bnepVar.indexPtr->deviceAddr);
                CsrBtCmBnepClearBnepTableIndex(&cmData->bnepVar.connectVar[theIndex]);
                CsrBtCmServiceManagerLocalQueueHandler(cmData);
                CsrBtCmDmLocalQueueHandler();
                break;
            }
        case CSR_BT_CM_BNEP_STATE_DISCONNECT_IND:
            { /* The CM has just ensure that SNIFF mode is supported after
                 the connection is released. If  requested link policy mode is different
                 from active, change to requested mode and clear the bnep connection table */
                CSR_BT_CM_STATE_CHANGE(cmData->bnepVar.connectVar[theIndex].state, CSR_BT_CM_BNEP_STATE_IDLE);
                CsrBtCmModeChangeReqMsgSend(cmData, cmData->bnepVar.indexPtr->deviceAddr);
                CsrBtCmBnepClearBnepTableIndex(cmData->bnepVar.indexPtr);
                CsrBtCmDmLocalQueueHandler();
                break;
            }
        default :
            {
                CsrBtCmServiceManagerLocalQueueHandler(cmData);
                CsrBtCmDmLocalQueueHandler();
                break;
            }
    }
}
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */

static void csrBtCmWriteLpSettingsCompleteCmHandler(cmInstanceData_t *cmData)
{
    DM_HCI_WRITE_LINK_POLICY_SETTINGS_CFM_T        *dmPrim;
    CsrUint8                                 numOfConnection;

    CsrUint8 numOfAcl    = returnNumOfAclConnection(cmData);
    dmPrim                = (DM_HCI_WRITE_LINK_POLICY_SETTINGS_CFM_T *) cmData->recvMsgP;
    numOfConnection     = CsrBtCmReturnNumOfConnectionsToPeerDevice(cmData, dmPrim->bd_addr);

    if (numOfAcl == 0 || numOfConnection == 0)
    { /* The local device has no ACL connection or no logical connections
         therefor a role switch cannot be performed. Just restore the queues */
        CsrBtCmDmLocalQueueHandler();
        CsrBtCmServiceManagerLocalQueueHandler(cmData);
    }
    else
    {
#ifdef CSR_BT_INSTALL_CM_INTERNAL_ROLE_CONTROL
        if (cmData->roleVar.roleSwitchFlag)
        {
            dm_hci_switch_role(&dmPrim->bd_addr, cmData->roleVar.requestedRole, NULL);
            cmData->dmVar.switchRoleDeviceAddr    = dmPrim->bd_addr;
        }
        else
#endif /* CSR_BT_INSTALL_CM_INTERNAL_ROLE_CONTROL */
        {
#ifndef CSR_BT_INSTALL_LP_POWER_TABLE
            if (CmDmGetActualMode(cmData, &dmPrim->bd_addr) == CSR_BT_ACTIVE_MODE &&
                cmData->dmVar.roleSwitchLinkMode != CSR_BT_ACTIVE_MODE)
            {
                /* The link were set in CSR_BT_ACTIVE_MODE only to make a Role Switch.
                 * This request change it back again */
                CmDmForceUpdatePowerTable(cmData,
                                          &dmPrim->bd_addr,
                                          CSR_BT_SNIFF_MODE,
                                          NULL,
                                          CM_PT_STATE_ROLE_SWITCH);
            }
            else
#endif /* !CSR_BT_INSTALL_LP_POWER_TABLE */
            {
#ifdef CSR_BT_INSTALL_CM_INTERNAL_ROLE_CONTROL
                cmData->dmVar.roleSwitchLinkMode    = CSR_BT_ACTIVE_MODE;

                if (numOfAcl > 1)
                { /* The local device has more than one ACL connection. */
                    aclTable   * nextAclConnectionElement;

                    returnNextAclConnectionElement(cmData, &nextAclConnectionElement);

                    if (nextAclConnectionElement)
                    {
                        if (nextAclConnectionElement->role == CSR_BT_MASTER_ROLE)
                        {/* The role is right just make sure that a
                            master slave switch is not supported */
                            cmData->roleVar.roleSwitchFlag        = FALSE;
                            nextAclConnectionElement->roleChecked = TRUE;
                            CsrBtCmDmWriteLpSettingsOnlyLowPowerMsgSend(cmData, nextAclConnectionElement->deviceAddr, CM_PLAYER);
                        }
                        else
                        { /* Need to change make a role switch */
                            CsrBtCmDmModeChangeRoleSwitchHandler(cmData, nextAclConnectionElement->deviceAddr);
                        }
                    }
                    else
                    { /* The process is finish restore the queues */
                        CsrBtCmDmLocalQueueHandler();
                        CsrBtCmServiceManagerLocalQueueHandler(cmData);
                    }
                }
                else
#endif /* CSR_BT_INSTALL_CM_INTERNAL_ROLE_CONTROL */
                { /* The local device has only one ACL connection. The process is
                     finish restore the queues */
                    CsrBtCmDmLocalQueueHandler();
                    CsrBtCmServiceManagerLocalQueueHandler(cmData);
                }
            }

        }
    }
}


static void csrBtCmWriteLpSettingsCompleteCmLinkPolicyHandler(cmInstanceData_t *cmData)
{
#ifndef CSR_BT_INSTALL_LP_POWER_TABLE
    DM_HCI_WRITE_LINK_POLICY_SETTINGS_CFM_T *dmPrim;
    CsrUint8 mode;

    dmPrim = (DM_HCI_WRITE_LINK_POLICY_SETTINGS_CFM_T *) cmData->recvMsgP;

    if (CmDmUpdatePowerTable(cmData,
                             &dmPrim->bd_addr,
                             CSR_BT_PASSIVE_MODE,
                             &mode,
                             CM_PT_STATE_LINK_POLICY_COMPLETE) == RESULT_CODE_CM_POWER_TABLE_UPDATED)
    {
        return;
    }
#endif /* !CSR_BT_INSTALL_LP_POWER_TABLE */

    CsrBtCmDmLocalQueueHandler();
    CsrBtCmServiceManagerLocalQueueHandler(cmData);
}

#ifdef CSR_BT_INSTALL_CM_INTERNAL_ROLE_CONTROL
static void csrBtCmWriteLpSettingsCompleteCmSwitchRolePlayerHandler(cmInstanceData_t *cmData)
{
    DM_HCI_WRITE_LINK_POLICY_SETTINGS_CFM_T *dmPrim;
    aclTable      *aclConnectionElement;
    aclRoleVars_t *roleVars;
    CsrBool        matched;

    dmPrim = (DM_HCI_WRITE_LINK_POLICY_SETTINGS_CFM_T *) cmData->recvMsgP;

    /* Match requested BD_ADDR against the complete event */
    matched = CsrBtBdAddrEq(&cmData->smVar.operatingBdAddr, &dmPrim->bd_addr);

    returnAclConnectionElement(cmData, dmPrim->bd_addr, &aclConnectionElement);

    roleVars = CsrBtCmDmGetAclRoleVars(aclConnectionElement);

    if(dmPrim->status == HCI_SUCCESS && aclConnectionElement && matched)
    {
        switch(roleVars->state)
        {
            case CM_SWITCH_ROLE_RESTORE_MSS:
                CSR_BT_CM_STATE_CHANGE(roleVars->state, CM_SWITCH_ROLE_IDLE);
                CSR_BT_CM_PLAYER_CHANGE(cmData->smVar.activePlayer, UNDEFINED_PLAYER);
                CsrBtCmRoleSwitchCfmSend(cmData->dmVar.appHandle,
                                         CSR_BT_RESULT_CODE_CM_SUCCESS,
                                         CSR_BT_SUPPLIER_CM,
                                         cmData->smVar.operatingBdAddr,
                                         aclConnectionElement->role,
                                         roleVars->roleType);
                CsrBtCmDmLocalQueueHandler();
                CsrBtCmServiceManagerLocalQueueHandler(cmData);
                break;

            case CM_SWITCH_ROLE_ALLOW_MSS:
                if (roleVars->role != aclConnectionElement->role)
                { /* Role doesn't match. We need to change it */
                    CSR_BT_CM_STATE_CHANGE(roleVars->state, CM_SWITCH_ROLE_DO);
                    dm_hci_switch_role(&cmData->smVar.operatingBdAddr, roleVars->role, NULL);
                    cmData->dmVar.appHandle = cmData->smVar.appHandle;
                    cmData->dmVar.switchRoleDeviceAddr = dmPrim->bd_addr;
                    break;
                }
                /* !! FALL THROUGH !!*/

                /* Crossover: the peer device changed the role while we preparing for it.
                   This should never happen since we only change the LP setting if we need to */

            case CM_SWITCH_ROLE_DO:
            default:
                CsrBtCmGeneralException(DM_PRIM,
                                        dmPrim->type,
                                        roleVars->state,
                                        "");
                CSR_BT_CM_PLAYER_CHANGE(cmData->smVar.activePlayer, UNDEFINED_PLAYER);
                CsrBtCmRoleSwitchCfmSend(cmData->dmVar.appHandle,
                                         CSR_BT_RESULT_CODE_CM_INTERNAL_ERROR,
                                         CSR_BT_SUPPLIER_CM,
                                         cmData->smVar.operatingBdAddr,
                                         CSR_BT_UNDEFINED_ROLE,
                                         CSR_BT_CM_SWITCH_ROLE_TYPE_INVALID);
                CsrBtCmDmLocalQueueHandler();
                CsrBtCmServiceManagerLocalQueueHandler(cmData);
                CsrBtCmDmAclRoleVarsClear(roleVars);
                break;
        }
    }
    else
    {
        CsrBtResultCode     resultCode;
        CsrBtSupplier resultSupplier;

        CSR_BT_CM_PLAYER_CHANGE(cmData->smVar.activePlayer, UNDEFINED_PLAYER);

        if (dmPrim->status == HCI_SUCCESS)
        {
            resultSupplier = CSR_BT_SUPPLIER_CM;
            if (!aclConnectionElement)
            {
                resultCode = CSR_BT_RESULT_CODE_CM_UNKNOWN_CONNECTION_IDENTIFIER;
            }
            else
            {
                resultCode = CSR_BT_RESULT_CODE_CM_INTERNAL_ERROR;
            }
        }
        else
        {
            resultSupplier = CSR_BT_SUPPLIER_HCI;
            resultCode     = (CsrBtResultCode) dmPrim->status;
        }

        CsrBtCmRoleSwitchCfmSend(cmData->dmVar.appHandle,
                                 resultCode,
                                 resultSupplier,
                                 cmData->smVar.operatingBdAddr,
                                 CSR_BT_UNDEFINED_ROLE,
                                 CSR_BT_CM_SWITCH_ROLE_TYPE_INVALID);

        CsrBtCmDmLocalQueueHandler();
        CsrBtCmServiceManagerLocalQueueHandler(cmData);
        CsrBtCmDmAclRoleVarsClear(roleVars);
    }
}
#endif /* CSR_BT_INSTALL_CM_INTERNAL_ROLE_CONTROL */
#endif /* CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */

void CsrBtCmDmHciWriteLpSettingsCompleteHandler(cmInstanceData_t *cmData)
{
#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
    switch(cmData->smVar.activePlayer)
    {
#ifndef EXCLUDE_CSR_BT_RFC_MODULE
        case RFC_PLAYER:
            {
                csrBtCmWriteLpSettingsCompleteRfcHandler(cmData);
                break;
            }
#endif /* EXCLUDE_CSR_BT_RFC_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
        case L2CAP_PLAYER:
            {
                cmWriteLpSettingsCompleteL2capHandler(cmData);
                break;
            }
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
        case BNEP_PLAYER:
            {
                csrBtCmWriteLpSettingsCompleteBnepHandler(cmData);
                break;
            }
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */

        case CM_PLAYER:
            {
                csrBtCmWriteLpSettingsCompleteCmHandler(cmData);
                break;
            }

        case CM_LINK_POLICY_PLAYER:
            {
                csrBtCmWriteLpSettingsCompleteCmLinkPolicyHandler(cmData);
                break;
            }

#ifdef CSR_BT_INSTALL_CM_INTERNAL_ROLE_CONTROL
        case CM_ROLE_SWITCH_PLAYER:
            {
                csrBtCmWriteLpSettingsCompleteCmSwitchRolePlayerHandler(cmData);
                break;
            }
#endif /* CSR_BT_INSTALL_CM_INTERNAL_ROLE_CONTROL */

        default:
            {
                CsrBtCmServiceManagerLocalQueueHandler(cmData);
                CsrBtCmDmLocalQueueHandler();
                break;
            }
    }

#else /* CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */

#ifdef CSR_BT_INSTALL_CM_LINK_POLICY
    DM_HCI_WRITE_LINK_POLICY_SETTINGS_CFM_T *dmPrim = (DM_HCI_WRITE_LINK_POLICY_SETTINGS_CFM_T*) cmData->recvMsgP;

    if (dmPrim->status != HCI_SUCCESS)
    {
        csrBtCmWriteLinkPolicyErrorIndMsgSend(cmData->smVar.appHandle,
                                              cmData->smVar.operatingBdAddr,
                                              dmPrim->status,
                                              CSR_BT_SUPPLIER_HCI);
    }
#endif /* CSR_BT_INSTALL_CM_LINK_POLICY */
    if (cmData->smVar.smMsgTypeInProgress == CSR_BT_CM_WRITE_LINK_POLICY_REQ)
    {
        CsrBtCmServiceManagerLocalQueueHandler(cmData);
    }
#endif /* !CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */
}

#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
#ifdef CSR_BT_INSTALL_CM_INTERNAL_ROLE_CONTROL
static void csrBtCmDmWriteLpSettingsReqRoleSwitchHandler(cmInstanceData_t *cmData)
{
    aclTable *aclConnectionElement;

    aclRoleVars_t *roleVars;
    CsrBtCmDmWriteLpSettingsReq *prim;

    prim = (CsrBtCmDmWriteLpSettingsReq *) cmData->recvMsgP;

    returnAclConnectionElement(cmData, prim->deviceAddr, &aclConnectionElement);
    roleVars = CsrBtCmDmGetAclRoleVars(aclConnectionElement);

    if (aclConnectionElement && CsrBtBdAddrEq(&cmData->smVar.operatingBdAddr, &prim->deviceAddr))
    {
        cmData->dmVar.appHandle = cmData->smVar.appHandle;
        CsrBtCmDmWriteLpSettingsOverrideMssMsgSend(cmData,
                                                   prim->deviceAddr,
                                                   prim->activePlayer,
                                                   prim->link_policy_settings);
    }
    else
    {
        /* No ACL/match. Restore queue */
        CsrBtResultCode     resultCode;

        if (!aclConnectionElement)
        {
            resultCode = CSR_BT_RESULT_CODE_CM_UNKNOWN_CONNECTION_IDENTIFIER;
        }
        else
        {
            resultCode = CSR_BT_RESULT_CODE_CM_INTERNAL_ERROR;
        }
        CSR_BT_CM_PLAYER_CHANGE(cmData->smVar.activePlayer, UNDEFINED_PLAYER);
        CsrBtCmRoleSwitchCfmSend(cmData->smVar.appHandle,
                                 resultCode,
                                 CSR_BT_SUPPLIER_CM,
                                 cmData->smVar.operatingBdAddr,
                                 CSR_BT_UNDEFINED_ROLE,
                                 CSR_BT_CM_SWITCH_ROLE_TYPE_INVALID);
        CsrBtCmDmLocalQueueHandler();
        CsrBtCmServiceManagerLocalQueueHandler(cmData);
        CsrBtCmDmAclRoleVarsClear(roleVars);
    }
}
#endif /* CSR_BT_INSTALL_CM_INTERNAL_ROLE_CONTROL */

static CsrBool cmDmMssSwitchRequestedFromPtChange(CmPtState ptState)
{
    switch (ptState)
    {
        case CM_PT_STATE_RFC_CONNECT_OR_DISCONNECT:
        case CM_PT_STATE_L2CAP_CONNECT_OR_DISCONNECT:
        case CM_PT_STATE_BNEP_CONNECT_OR_DISCONNECT:
        case CM_PT_STATE_LINK_POLICY_REQ:
        case CM_PT_STATE_LE_PHYSICAL_LINK_STATUS_REQ:
        case CM_PT_STATE_LINK_POLICY_COMPLETE:
            return TRUE;
        default:
            return FALSE;
    }
}

void CsrBtCmDmWriteLpSettingsReqHandler(cmInstanceData_t *cmData)
{
    CsrBtCmDmWriteLpSettingsReq *prim;

    prim = (CsrBtCmDmWriteLpSettingsReq *) cmData->recvMsgP;

    switch(prim->activePlayer)
    {
#ifdef CSR_BT_INSTALL_CM_INTERNAL_ROLE_CONTROL
        case CM_ROLE_SWITCH_PLAYER:
            csrBtCmDmWriteLpSettingsReqRoleSwitchHandler(cmData);
            break;
#endif

        default:
        {
            /* Check if the caller of this wants to set link policy to a specified value. */
            if (prim->link_policy_settings != LINK_POLICY_MASK)
            {
                /* Check if the LP settings was requested as a part of power table change procedure,
                 * if so then clear the state of power table. */
                CmPtState ptState = cmDmMssSwitchRequestedFromPtChange(CM_PT_STATE_GET(cmData)) ?
                                    CM_PT_STATE_IDLE :
                                    CM_PT_STATE_GET(cmData);

                /* Update new power table state. */
                CM_PT_STATE_SET(cmData, ptState);
                CsrBtCmDmWriteLpSettingsForceLinkPolicyMsgSend(cmData,
                                                               prim->deviceAddr,
                                                               prim->activePlayer,
                                                               prim->link_policy_settings);
            }
            else
            {
                /* The caller is not bothered about setting the link policy, set to all. */
                CsrBtCmDmWriteLpSettingsAllMsgSend(cmData,
                                                   prim->deviceAddr,
                                                   prim->activePlayer);
            }
        }
    }
}

void CsrBtCmDmWriteLpSettingsReqMsgSend(CsrBtDeviceAddr deviceAddr,
                                        CsrUint8 player,
                                        link_policy_settings_t link_policy_settings)
{ /* Build the intern CM_WRITE_LP_SETTINGS_REQ primitive and send it to itself.
     Hereby it will be control by the CsrBtCmDmProvider */
    CsrBtCmDmWriteLpSettingsReq *prim;

    prim                       = (CsrBtCmDmWriteLpSettingsReq *) CsrPmemAlloc(sizeof(CsrBtCmDmWriteLpSettingsReq));
    prim->type                 = CSR_BT_CM_DM_WRITE_LP_SETTINGS_REQ;
    prim->deviceAddr           = deviceAddr;
    prim->link_policy_settings = link_policy_settings;
    prim->activePlayer         = player;
    CsrBtCmPutMessage(CSR_BT_CM_IFACEQUEUE, prim);
}
#endif /* CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */

#ifdef CSR_BT_INSTALL_CM_LINK_POLICY
#ifndef CSR_BT_INSTALL_LP_POWER_TABLE
static void cmUpdateSniffSettingsForAllLinks(cmInstanceData_t *cmData,
                                             CsrBtDeviceAddr *deviceAddr,
                                             CsrBtSniffSettings *sniffSettings)
{
#ifndef EXCLUDE_CSR_BT_RFC_MODULE
    {
        cmRfcConnElement *currentRfcElem;

        for (currentRfcElem = CM_RFC_GET_FIRST(cmData->rfcVar.connList); currentRfcElem; currentRfcElem = currentRfcElem->next)
        { /* Search through the RFC connection list                     */
            if (currentRfcElem->cmRfcConnInst)
            {
                if (currentRfcElem->cmRfcConnInst->state == CSR_BT_CM_RFC_STATE_CONNECTED)
                { /* The application has a RFCOMM connection */
                    if(CsrBtBdAddrEq(deviceAddr, &(currentRfcElem->cmRfcConnInst->deviceAddr)))
                    { /* The given device address is right. Build and send
                         CSR_BT_CM_RFC_MODE_CHANGE_IND to the application with the current
                         link policy. */
                        cmRfcConnInstType *theLogicalLink   = currentRfcElem->cmRfcConnInst;

                        theLogicalLink->sniffSettings    = *sniffSettings;
                        theLogicalLink->lowPowerPriority = LOW_POWER_DEFAULT_PRIORITY;
                    }
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_RFC_MODULE */

#ifndef EXCLUDE_CSR_BT_L2CA_MODULE
    {
        cmL2caConnElement *currentL2caElem;

        for (currentL2caElem = CM_L2CA_GET_FIRST(cmData->l2caVar.connList); currentL2caElem; currentL2caElem = currentL2caElem->next)
        { /* Search through the L2ca connection list                     */
            if (currentL2caElem->cmL2caConnInst)
            {
                if (currentL2caElem->cmL2caConnInst->state == CSR_BT_CM_L2CAP_STATE_CONNECTED)
                { /* The application has a L2cap connection */
                    if(CsrBtBdAddrEq(deviceAddr, &(currentL2caElem->cmL2caConnInst->deviceAddr)))
                    { /* The given device address is right. Build and send
                         CSR_BT_CM_L2CA_MODE_CHANGE_IND to the application with the current
                         link policy. */
                        cmL2caConnInstType *l2CaConnection = currentL2caElem->cmL2caConnInst;

                        l2CaConnection->sniffSettings = *sniffSettings;
                        l2CaConnection->lowPowerPriority = LOW_POWER_DEFAULT_PRIORITY;
                    }
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_L2CA_MODULE */

#ifndef EXCLUDE_CSR_BT_BNEP_MODULE
    {
        CsrUintFast8 i;

        for( i = 0; i < CSR_BT_MAX_NUM_OF_SIMULTANEOUS_BNEP_CONNECTIONS; i++ )
        { /* Search through the BNEP connection table */

            if (cmData->bnepVar.connectVar[i].state == CSR_BT_CM_BNEP_STATE_CONNECTED ||
                cmData->bnepVar.connectVar[i].state == CSR_BT_CM_BNEP_STATE_DISCONNECT_INIT)
            {
                if(CsrBtBdAddrEq(deviceAddr, &(cmData->bnepVar.connectVar[i].deviceAddr)))
                { /* Thie given address is right. Store the actual link policy in the connection table */
                    bnepTable *bnepConnection;

                    bnepConnection    = &(cmData->bnepVar.connectVar[i]);
                    bnepConnection->sniffSettings = *sniffSettings;
                    bnepConnection->lowPowerPriority = LOW_POWER_DEFAULT_PRIORITY;
                }
            }
        }
    }
#endif /* EXCLUDE_CSR_BT_BNEP_MODULE */
}
#endif /* !CSR_BT_INSTALL_LP_POWER_TABLE */

void CsrBtCmWriteLinkPolicyReqHandler(cmInstanceData_t *cmData)
{
    CsrBtCmWriteLinkPolicyReq *cmPrim = (CsrBtCmWriteLinkPolicyReq *) cmData->recvMsgP;

    if (CsrBtBdAddrEqZero(&cmPrim->deviceAddr))
    {
        link_policy_settings_t policy = cmPrim->linkPolicySetting & (ENABLE_SNIFF | ENABLE_MS_SWITCH);

#ifndef CSR_BT_INSTALL_LP_POWER_TABLE
        if (cmPrim->sniffSettings)
        {
            cmData->dmVar.defaultSniffSettings  = *cmPrim->sniffSettings;
        }
#endif

        if (cmPrim->setupLinkPolicySetting)
        { /* cmData->dmVar.defaultLinkPolicySettings is ONLY use to control 
             which low Power modes are supported */ 
            cmData->dmVar.defaultLinkPolicySettings = cmPrim->linkPolicySetting;
#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
            cmData->dmVar.defaultLinkPolicySettings &= ENABLE_SNIFF;
#endif
        }

        insertDefaultLinkPolicySettings(cmData);
        cmData->smVar.appHandle = cmPrim->appHandle;
        cmData->smVar.operatingBdAddr = cmPrim->deviceAddr;

        /* This sets both DM and HCI default policy */
        dm_set_default_link_policy_req(policy, policy, NULL);
    }
    else
    {
        aclTable *aclConnectionElement;
        returnAclConnectionElement(cmData,
                                   cmPrim->deviceAddr,
                                   &aclConnectionElement);

        if (aclConnectionElement)
        {
            if (cmPrim->setupLinkPolicySetting)
            {
                aclConnectionElement->linkPolicySettings = cmPrim->linkPolicySetting;
#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
                aclConnectionElement->linkPolicySettings  &= ENABLE_SNIFF;
#endif
            }

#ifdef CSR_BT_INSTALL_LP_POWER_TABLE
            CsrBtCmDmWriteLpSettingsForceLinkPolicyMsgSend(cmData,
                                                           cmPrim->deviceAddr,
                                                           CM_LINK_POLICY_PLAYER,
                                                           aclConnectionElement->linkPolicySettings);

#else /* CSR_BT_INSTALL_LP_POWER_TABLE */
            /* For internal power table, we need to update the sniffSettings given by the application
             * to all the other existing service connections. */
            if (cmPrim->sniffSettings)
            {
                aclConnectionElement->sniffSettings = *cmPrim->sniffSettings;
                cmUpdateSniffSettingsForAllLinks(cmData, &aclConnectionElement->deviceAddr, cmPrim->sniffSettings);
            }

            if (aclConnectionElement->mode == CSR_BT_ACTIVE_MODE)
            {
#ifdef CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL
                /* If we are in ACTIVE mode then the link policy request can immediately go.*/
                CsrBtCmDmWriteLpSettingsReqMsgSend(aclConnectionElement->deviceAddr,
                                                   CM_LINK_POLICY_PLAYER,
                                                   LINK_POLICY_MASK /* enable all settings */);
#endif /* CSR_BT_INSTALL_CM_INTERNAL_LP_CONTROL */
            }
            else
            {
                /* We need to move to ACTIVE mode first, before continuing with the LP settings. */
                CmDmForceUpdatePowerTable(cmData,
                                          &aclConnectionElement->deviceAddr,
                                          CSR_BT_ACTIVE_MODE,
                                          NULL,
                                          CM_PT_STATE_LINK_POLICY_REQ);
            }
#endif /* !CSR_BT_INSTALL_LP_POWER_TABLE */
        }
        else
        {
            csrBtCmWriteLinkPolicyErrorIndMsgSend(cmPrim->appHandle,
                                                  cmPrim->deviceAddr,
                                                  CSR_BT_RESULT_CODE_CM_UNKNOWN_CONNECTION_IDENTIFIER,
                                                  CSR_BT_SUPPLIER_CM);
            CsrBtCmServiceManagerLocalQueueHandler(cmData);
        }
    }
}

#ifdef CSR_BT_INSTALL_CM_READ_LP
void CsrBtCmReadLinkPolicyReqHandler(cmInstanceData_t *cmData)
{
    CsrBtCmReadLinkPolicyReq        * cmPrim;
    CsrBtCmReadLinkPolicyCfm        * prim;

    cmPrim                          = (CsrBtCmReadLinkPolicyReq *) cmData->recvMsgP;
    prim                            = (CsrBtCmReadLinkPolicyCfm *)CsrPmemZalloc(sizeof(CsrBtCmReadLinkPolicyCfm));
    prim->type                      = CSR_BT_CM_READ_LINK_POLICY_CFM;
    prim->deviceAddr                = cmPrim->deviceAddr;
    prim->resultSupplier            = CSR_BT_SUPPLIER_CM;

    if (CsrBtBdAddrEqZero(&cmPrim->deviceAddr))
    {
        prim->resultCode            = CSR_BT_RESULT_CODE_CM_SUCCESS;
        prim->actualMode            = CSR_BT_ACTIVE_MODE;
        prim->linkPolicySetting     = cmData->dmVar.defaultLinkPolicySettings;

#ifndef CSR_BT_INSTALL_LP_POWER_TABLE
        prim->sniffSettings         = cmData->dmVar.defaultSniffSettings;
#endif
    }
    else
    {
        aclTable       * aclConnectionElement;

        returnAclConnectionElement(cmData, cmPrim->deviceAddr, &aclConnectionElement);

        if (aclConnectionElement)
        {
            prim->resultCode        = CSR_BT_RESULT_CODE_CM_SUCCESS;
#ifndef CSR_BT_INSTALL_LP_POWER_TABLE
            prim->actualMode        = CmDmGetActualMode(cmData, &cmPrim->deviceAddr);
            prim->sniffSettings     = aclConnectionElement->sniffSettings;
#endif
            prim->linkPolicySetting = aclConnectionElement->linkPolicySettings;
        }
        else
        {
            prim->resultCode        = CSR_BT_RESULT_CODE_CM_UNKNOWN_CONNECTION_IDENTIFIER;
            prim->actualMode        = CSR_BT_ACTIVE_MODE;
            prim->linkPolicySetting = cmData->dmVar.defaultLinkPolicySettings;

#ifndef CSR_BT_INSTALL_LP_POWER_TABLE
            prim->sniffSettings     = cmData->dmVar.defaultSniffSettings;
#endif
        }

    }
    CsrBtCmPutMessage(cmPrim->appHandle, prim);
    CsrBtCmServiceManagerLocalQueueHandler(cmData);
}
#endif /* CSR_BT_INSTALL_CM_READ_LP */
#endif /* CSR_BT_INSTALL_CM_LINK_POLICY */

void CsrBtCmDmHciWriteDefaultLinkPolicySettingsCompleteHandler(cmInstanceData_t *cmData)
{
    DM_HCI_WRITE_DEFAULT_LINK_POLICY_SETTINGS_CFM_T *dmPrim;
    dmPrim = (DM_HCI_WRITE_DEFAULT_LINK_POLICY_SETTINGS_CFM_T*)cmData->recvMsgP;

    if (cmData->globalState == CSR_BT_CM_STATE_NOT_READY)
    {
        if (dmPrim->status != HCI_SUCCESS)
        {
            /* Can't do much but throw an exception and continue */
            CsrBtCmGeneralException(DM_PRIM,
                                    dmPrim->type,
                                    0,
                                    "HCI command failed");
        }

        /* We are currently in CM initialization phase, continue with the sequence. */
        CmInitSequenceHandler(cmData,
                              CM_INIT_SEQ_WRITE_DEFAULT_LINK_POLICY_SETTINGS_CFM,
                              dmPrim->status,
                              CSR_BT_SUPPLIER_HCI);
    }
    else
    {
        /* Normal CM operation. Send error indication to application
         * in case the HCI command failed, otherwise keep quiet as
         * there is no CFM for this */
#ifdef CSR_BT_INSTALL_CM_LINK_POLICY
        if((dmPrim->status != HCI_SUCCESS) &&
           (cmData->smVar.appHandle != CSR_BT_CM_IFACEQUEUE))
        {
            csrBtCmWriteLinkPolicyErrorIndMsgSend(cmData->smVar.appHandle,
                                                  cmData->smVar.operatingBdAddr,
                                                  dmPrim->status,
                                                  CSR_BT_SUPPLIER_HCI);
        }
#endif
        /* Unlock queue */
        CsrBtCmServiceManagerLocalQueueHandler(cmData);
    }
}

#ifdef CSR_BT_INSTALL_CM_SWITCH_ROLE_PUBLIC
void CsrBtCmAlwaysMasterDevicesReqHandler(cmInstanceData_t *cmData)
{
    CsrBtCmAlwaysMasterDevicesReq * cmPrim = (CsrBtCmAlwaysMasterDevicesReq *) cmData->recvMsgP;
    cmData->dmVar.appHandle                = cmPrim->phandle; 
    cmData->dmVar.operatingBdAddr          = cmPrim->deviceAddr; 
    dm_lp_write_always_master_devices_req(cmPrim->operation,
                                          &cmPrim->deviceAddr,
                                          NULL);
}   

void CsrBtCmAlwaysMasterDevicesCfmHandler(cmInstanceData_t *cmData)
{
    DM_LP_WRITE_ALWAYS_MASTER_DEVICES_CFM_T *dmPrim = (DM_LP_WRITE_ALWAYS_MASTER_DEVICES_CFM_T*) cmData->recvMsgP;

    CsrBtCmAlwaysMasterDevicesCfm *cmPrim = (CsrBtCmAlwaysMasterDevicesCfm *)
                                             CsrPmemAlloc(sizeof(CsrBtCmAlwaysMasterDevicesCfm));


    cmPrim->type            = CSR_BT_CM_ALWAYS_MASTER_DEVICES_CFM;
    cmPrim->deviceAddr      = cmData->dmVar.operatingBdAddr;
    cmPrim->resultSupplier  = CSR_BT_SUPPLIER_CM;
    cmPrim->resultCode      = CSR_BT_RESULT_CODE_CM_SUCCESS;  
    
    if(dmPrim->status != HCI_SUCCESS)
    {
        cmPrim->resultSupplier = CSR_BT_SUPPLIER_HCI;
        cmPrim->resultCode     = (CsrBtResultCode) dmPrim->status;
    }
    CsrBtCmPutMessage(cmData->dmVar.appHandle, cmPrim);
    CsrBtCmDmLocalQueueHandler();
}
#endif /* CSR_BT_INSTALL_CM_SWITCH_ROLE_PUBLIC */


